﻿using System;
using System.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;
using System.Threading;

namespace _344A
{
    /*
     *Class to store Event Information for the event 
     *for which we want to create a booking.
     */
    class EventInfo
    {
        public int id;          //unique event ID
        public String title;    //event nane
        public String date;     //event date and start time
        public String time;     //event duration
        public int capacity;    //capacity of the event = number of tickets
        public bool status;     //to check if the event is open for registration (T) or not (F)
        public String hall;     //which hall the event will take place in
        public int currSeat;    //the current number of seats booked
    }
    /*
     *Class for all Booking-related actions 
     */
    public partial class CreateBooking : System.Web.UI.Page
    {
        SqlConnection con = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["Database1ConnectionString"].ConnectionString);
        SqlCommand cmd;
        SqlDataAdapter DB;
        DataSet DS = new DataSet();
        DataTable DT = new DataTable(); //the above are all SQL connection objects for the TDG
        EventInfo ev = new EventInfo(); //an event information object
        static Semaphore sem = new Semaphore(1, 1); //semaphore to ensure only one client can modify the database (1 semaphore, 1 available)
        /*
         *Method loaded on page load to setup proper variables
         */
        protected void Page_Load(object sender, EventArgs e)
        {
            checkCount();                                           //check the number of tickets currently booked
            getEvent();                                             //get the event information
            SetName.InnerHtml = "Create Booking for "+ev.title;
            if (!ev.status)                                         //if the event is full, do not allow any actions
            {
                BookForm.Style.Add("color", "red");
                BookForm.InnerHtml = "Unfortunately, this Event is now closed to bookings.";
            }
        }
        /*
         *Method to load event data, based on event ID passed in QueryString
         */
        protected void getEvent()
        {
            con.Open();
            string CommandText = "select * from Event where id =\'" + Request.QueryString["id"] + "\';";    //attempt to get the event data from the SQL server
            try
            {
                DB = new SqlDataAdapter(CommandText, con);
                DS.Reset();
                DB.Fill(DT);
                DataRow DR = DT.Rows[0];
                ev.id = DR.Field<int>("id");                                //fill in the appropriate class values with the row data
                ev.title = DR.Field<string>("title");
                ev.date = Convert.ToString(DR.Field<DateTime>("date"));
                ev.time = Convert.ToString(DR.Field<TimeSpan>("time"));
                ev.capacity = DR.Field<int>("capacity");
                ev.status = DR.Field<bool>("status");
                ev.hall = DR.Field<string>("hall");
                ename.InnerHtml = ev.title;                                 //update these values in the view
                ehall.InnerHtml = ev.hall;
                edate.InnerHtml = ev.date;
                edur.InnerHtml = ev.time;
                etix.InnerHtml = Convert.ToString(ev.capacity - ev.currSeat);
            }
            catch
            {
                ev.title = "Unknown Event";                                 //default to these values for error messages if an exception is thrown
                ev.status = true;
                BookForm.Style.Add("color", "red");
                BookForm.InnerHtml = "Connection to Database Failed! Please check that a valid Event was selected, or contact support.";
            }
            con.Close();
        }
        /*
         *Method to create a booking
         */ 
        protected void createBooking(object sender, EventArgs e)
        {
            if (validateEntry())    //begin by validating the entry, namely the name of the client and the number of tickets
            {
                sem.WaitOne();      //lock the semaphore
                checkCount();       //recheck the count and event data to make sure we can still book the event (concurrency check)
                getEvent();
                if (!ev.status)     //in case the event has closed...
                {
                    BookForm.Style.Add("color", "red");
                    BookForm.InnerHtml = "Unfortunately, this Event is now closed to bookings.";
                }
                else if(Convert.ToInt32(notix.Text) > (ev.capacity - ev.currSeat))  //in case the number of tickets requested is no longer available...
                {
                    res.InnerHtml = "There are no longer enough tickets to accomodate this booking. Please check the number of available tickets and try again.";
                }
                else
                {
                    
                    con.Open();
                    if (Convert.ToInt32(notix.Text) + ev.currSeat == ev.capacity)   //if, with this transaction, we close the event to more bookings, set the flag
                    {
                        cmd = con.CreateCommand();
                        string CommandText = "update Event set status=\'False\' where id = \'" + ev.id + "\';";
                        cmd.CommandText = CommandText;
                        try
                        {
                            cmd.ExecuteNonQuery();
                        }
                        catch       //generic error message
                        {
                            BookForm.Style.Add("color", "red");
                            BookForm.InnerHtml = "Connection to Database Failed! Please check that a valid Event was selected, or contact support.";
                            con.Close();    //close connection
                            sem.Release(1); //release semaphore
                            return;  //stop execution here (do not continue execution)
                        }
                    }
                    string confirmation = "<strong>Booking Successful!</strong><br/>Confirmation Number(s):<br/> "; //build the results page
                    bool pass = true;
                    for (int i = 0; i < Convert.ToInt32(notix.Text); i++)   //for each ticket the customer requested...
                    {
                        cmd = con.CreateCommand();
                        ev.currSeat++;
                        string CommandText = "insert into Booking (eventId, firstName, lastName, seatNo) values (\'" + ev.id + "\',\'" + fname.Text + "\',\'" + lname.Text + "\',\'" + (ev.currSeat) + "\');";
                        cmd.CommandText = CommandText;
                        try
                        {
                            cmd.ExecuteNonQuery();
                            CommandText = "SELECT TOP (1) confirmation FROM Booking ORDER BY confirmation DESC;";   //create a booking and return its confrimation number
                            cmd.CommandText = CommandText;
                            int id = (int)cmd.ExecuteScalar();
                            confirmation = confirmation + " " + id+"<br/>";
                        }
                        catch       //generic error message
                        {
                            pass = false;
                            BookForm.Style.Add("color", "red");
                            BookForm.InnerHtml = "Connection to Database Failed! Please check that a valid Event was selected, or contact support.";
                            break;  //stop execution here (do not continue loop)
                        }
                    }
                    con.Close();
                    sem.Release(1);    //release the semaphore; with the try catch statements, this ensures starvation does not occur even if an exception is thrown
                    if (pass)   //assuming all went well, print the confirmation message
                    {
                        BookForm.Style.Add("color", "blue");
                        BookForm.InnerHtml = confirmation;
                    }
                }
            }
        }
        /*
         *Method to check the number of bookings made from the database
         */ 
        protected void checkCount()
        {
            con.Open();
            string CommandText = "select count(*) as count from Booking where eventId =\'" + Request.QueryString["id"] + "\';";
            cmd = con.CreateCommand();
            cmd.CommandText = CommandText;
            try
            {
                ev.currSeat = (int)cmd.ExecuteScalar(); //evaluate the number of entries corresponding to the active event
            }
            catch       //generic error message
            {
                ev.title = "Unknown Event";
                ev.status = true;
                BookForm.Style.Add("color", "red");
                BookForm.InnerHtml = "Bookings Error! Please check that a valid Event was selected, or contact support.";
            }
            con.Close();
        }
        /*
         *Method to validate client entry (on names and # tickets) 
         */ 
        protected bool validateEntry()
        {
            double Num;
            if (fname.Text == "" || lname.Text == "" || notix.Text == "")   //if any field is left blank...
            {
                res.InnerHtml = "One or more fields are empty! Please correct the input and try again!";
                return false;
            }
            else if (!double.TryParse(notix.Text, out Num) || Convert.ToInt32(notix.Text) < 1 || Convert.ToInt32(notix.Text) > (ev.capacity - ev.currSeat)) //if the number of tickets is NaN or an invalid value
            {
                res.InnerHtml = "An invalid number of tickets is specified. Please correct the input and try again!";
                return false;
            }
            else
            {
                res.InnerHtml = "";
                return true;
            }
        }
    }
}